%{
/*
 *   Frunk yacc/bison parser
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "utils.h"

extern BOOLEAN AddDevice(PCONFIG_DATA pcd, PDEVICE pdev);
extern BOOLEAN AddInterface(PDLLIST pdllDevices, PDEVICE pdev, PINTERFACE pif);
extern BOOLEAN ResetConfig(PCONFIG_DATA pcd);
extern BOOLEAN ResetDevice(PDEVICE pdev);
extern BOOLEAN ResetInterface(PINTERFACE pif);
extern void CleanupConfig(PCONFIG_DATA pcd);

extern void yyerror(const char *format, ...);
extern int yylex();

static CONFIG_DATA cd;
static DEVICE device;
static INTERFACE interface;

%}

%union
{
  int intval;
  char *string;
}

%token TOK_ADDRESS
%token TOK_COMMENT
%token TOK_COMMUNITY
%token TOK_DESCRIPTION
%token TOK_DEVICE
%token TOK_DIRECTORY
%token TOK_EMAIL
%token TOK_FROM
%token TOK_HTMLOUTPUT
%token TOK_INTERFACE
%token TOK_NOTIFY
%token TOK_POLLING
%token TOK_REMINDER
%token TOK_TITLE
%token TOK_TO
%token TOK_URL
%token <intval> TOK_NUMBER
%token <string> TOK_STRING

%type <intval> device_definition
%type <intval> interface_definition
%type <intval> polling_definition

%type <string> address_definition
%type <string> comment_definition
%type <string> community_definition
%type <string> compound_string
%type <string> description_definition
%type <string> directory_definition
%type <string> email_from_definition
%type <string> email_to_definition
%type <string> title_definition
%type <string> url_definition

%%

configuration:
	/* null */
|	configuration config_statement
;

config_statement:
	device_definition
	{
	  if($1<=0)
	  {
	    yyerror("non-positive device id specified");
	    YYABORT;
	  }
	  device.nId=$1;
	  if(!AddDevice(&cd, &device))
	  {
	    YYABORT;
	  }
	}
|	directory_definition
        {
	  if(cd.pstrWorkingDirectory)
	  {
	    yyerror("duplicate working directory from specified");
	    free($1);
	    YYABORT;
	  }
	  cd.pstrWorkingDirectory=$1;
        }
|	html_output_definition
|	notify_definition
|	polling_definition
	{
	  if(cd.nSleepInterval>0)
	  {
	    yyerror("duplicate polling specified");
	    YYABORT;
	  }
	  if($1<=0)
          {
	    yyerror("non-positive polling interval specified");
            YYABORT;
	  }
	  cd.nSleepInterval=$1;
	}
;

directory_definition:
	TOK_DIRECTORY compound_string ';'
	{
	  $$=$2;
	}
;

html_output_definition:
	TOK_HTMLOUTPUT ';'
	{
	  if(cd.bHTMLOutput)
	  {
	    yyerror("duplicate HTML output setting specified");
	    YYABORT;
	  }
	  ++cd.bHTMLOutput;
	}
;

notify_definition:
	TOK_NOTIFY '{' notify_attributes_list '}'
;

notify_attributes_list:
	/* null */
|	notify_attributes_list notify_attribute
;

notify_attribute:
	email_definition
;

email_definition:
	TOK_EMAIL '{' email_attributes_list '}'
;

email_attributes_list:
	/* null */
|	email_attributes_list email_attribute
;

email_attribute:
	email_from_definition
	{
	  if(cd.pstrMailEventsFrom)
	  {
	    yyerror("duplicate email from specified");
	    free($1);
	    YYABORT;
	  }
	  cd.pstrMailEventsFrom=$1;
	}
|	email_to_definition
	{
	  if(cd.pstrMailEventsTo)
	  {
	    yyerror("duplicate email to specified");
	    free($1);
	    YYABORT;
	  }
	  cd.pstrMailEventsTo=$1;
	}
|	reminder_definition
;

email_from_definition:
	TOK_FROM compound_string ';'
	{
	  $$=$2;
	}
;

email_to_definition:
	TOK_TO compound_string ';'
	{
	  $$=$2;
	}
;

reminder_definition:
	TOK_REMINDER TOK_NUMBER ',' TOK_NUMBER ';'
	{
	  if(cd.nMinReminderTime>0)
	  {
	    yyerror("duplicate reminder specified");
	    YYABORT;
	  }
	  if($2<=0)
	  {
	    yyerror("non-positive min reminder time specified");
	    YYABORT;
	  }
	  if($4<$2)
	  {
	    yyerror("max less than min reminder time");
	    YYABORT;
	  }
	  cd.nMinReminderTime=$2*60;
	  cd.nMaxReminderTime=$4*60;
	}
;

polling_definition:
	TOK_POLLING TOK_NUMBER ';'
	{
          /* Accept in minutes, convert to seconds */
	  $$=$2*60;
	}
;

device_definition:
	TOK_DEVICE TOK_NUMBER '{' device_attributes_list '}'
	{
	  $$=$2;
	}
;

device_attributes_list:
	/* null */
|	device_attributes_list device_attribute
;

device_attribute:
	address_definition
	{
	  if(device.pstrAddress)
	  {
	    yyerror("duplicate address specified");
	    free($1);
	    YYABORT;
	  }
	  device.pstrAddress=$1;
	}
|	community_definition
	{
	  if(device.pstrCommunity)
	  {
	    yyerror("duplicate community specified");
	    free($1);
	    YYABORT;
	  }
	  device.pstrCommunity=$1;
	}
|	interface_definition
	{
	  interface.nId=$1;
	  if(!AddInterface(&cd.dllDevices, &device, &interface))
	  {
	    YYABORT;
	  }
	}
;

address_definition:
	TOK_ADDRESS compound_string ';'
	{
	  $$=$2;
	}
;

community_definition:
	TOK_COMMUNITY compound_string ';'
	{
	  $$=$2;
	}
;

interface_definition:
	TOK_INTERFACE TOK_NUMBER '{' interface_attributes_list '}'
	{
	  $$=$2;
	}
;

interface_attributes_list:
	/* null */
|	interface_attributes_list interface_attribute
;

interface_attribute:
	comment_definition
	{
	  if(interface.pstrComment)
	  {
	    yyerror("duplicate comment specified");
	    free($1);
	    YYABORT;
	  }
	  interface.pstrComment=$1;
	}
|	description_definition
	{
	  if(interface.pstrDescr)
	  {
	    yyerror("duplicate description specified");
	    free($1);
	    YYABORT;
	  }
	  interface.pstrDescr=$1;
	}
|	title_definition
	{
	  if(interface.pstrTitle)
	  {
	    yyerror("duplicate title specified");
	    free($1);
	    YYABORT;
	  }
	  interface.pstrTitle=$1;
	}
|	url_definition
	{
	  if(interface.pstrURL)
	  {
	    yyerror("duplicate URL specified");
	    free($1);
	    YYABORT;
	  }
	  interface.pstrURL=$1;
	}
;

comment_definition:
	TOK_COMMENT compound_string ';'
	{
	  $$=$2;
	}
;

description_definition:
	TOK_DESCRIPTION compound_string ';'
	{
	  $$=$2;
	}
;

title_definition:
	TOK_TITLE compound_string ';'
	{
	  $$=$2;
	}
;

url_definition:
	TOK_URL compound_string ';'
	{
	  $$=$2;
	}
;

compound_string:
	TOK_STRING
	{
	  $$=strdup($1);
	  if(!$$)
	  {
	    yyerror("memory allocation failure");
	    YYABORT;
	  }
	}
|	compound_string TOK_STRING
	{
	  $$=malloc(strlen($1)+strlen($2)+1);
	  if(!$$)
          {
	    free($1);
	    yyerror("memory allocation failure");
	    YYABORT;
	  }
	  strcpy($$, $1);
          strcat($$, $2);
	  free($1);
	}
;

%%

BOOLEAN
Config_Parse(PCONFIG_DATA pConfigData, char szConfigFile[])
{
  extern FILE *yyin;
  int errors=FALSE;

  yyin=NULL;

  ResetConfig(&cd);
  ResetDevice(&device);
  ResetInterface(&interface);

  yyin=fopen(szConfigFile, "r");
  if(!yyin)
  {
    errors=TRUE;
    goto end;
  }

  errors=yyparse();
  if(errors)
  {
    goto end;
  }

  /* Complete with defaults, if necessary */
  cd.nSleepInterval=(cd.nSleepInterval>0 ? cd.nSleepInterval : 5*60);
  cd.nMinReminderTime=(cd.nMinReminderTime>0 ? cd.nMinReminderTime : 30*60);
  cd.nMaxReminderTime=(cd.nMaxReminderTime>0 ? cd.nMaxReminderTime : 24*60*60);
  cd.pstrMailEventsFrom=(cd.pstrMailEventsFrom ?
                         cd.pstrMailEventsFrom : strdup("frunk"));
  cd.pstrMailEventsTo=(cd.pstrMailEventsTo ?
                       cd.pstrMailEventsTo : strdup("frunk"));
  cd.pstrConfigFile=strdup(szConfigFile);
  cd.pstrWorkingDirectory=(cd.pstrWorkingDirectory ?
                           cd.pstrWorkingDirectory :
                           strdup(DEFAULT_FRUNKD_DIR));

  if(!cd.pstrMailEventsFrom || !cd.pstrMailEventsTo ||
     !cd.pstrConfigFile || !cd.pstrWorkingDirectory)
  {
    errors=TRUE;
    goto end;
  }

  *pConfigData=cd;

end:
  if(errors)
  {
    CleanupConfig(&cd);
  }
  if(yyin)
  {
    fclose(yyin);
  }
  return !errors;
}
